Skip to content

fix(kafka): fix use-after-free on task cancellation during kafka producer Send#1299

Open
disaykin wants to merge 2 commits into
userver-framework:developfrom
disaykin:kafka-producer-cancellation-fix
Open

fix(kafka): fix use-after-free on task cancellation during kafka producer Send#1299
disaykin wants to merge 2 commits into
userver-framework:developfrom
disaykin:kafka-producer-cancellation-fix

Conversation

@disaykin

Copy link
Copy Markdown
Contributor

Fixes a Use-After-Free vulnerability in the asynchronous Kafka Producer API when a task is cancelled before librdkafka flushes its queue.

The kafka::Producer::Send API accepts parameters as views (string_view, zstring_view, HeaderViews). These views point to data owned by the caller. Inside Send, raw pointers from these views are passed down to librdkafka, which stores them in its internal asynchronous queues for background delivery.

If a client connection drops, a cascade cancellation of tasks occurs. This leads to a premature return from kafka::Producer::Send, causing the caller to destroy the original payload memory. However, librdkafka still retains these pointers and attempts to access them during subsequent network flushes to the broker, resulting in memory corruption.

To fix this, we ensure that Producer::Send is blocked from returning prematurely and cannot exit until the background SendImpl completes its interaction with librdkafka.


Note: by creating a PR or an issue you automatically agree to the CLA. See CONTRIBUTING.md. Feel free to remove this note, the agreement holds.

…ucer Send

Fixes a Use-After-Free vulnerability in the asynchronous Kafka Producer API when
a task is cancelled before librdkafka flushes its queue.

The `kafka::Producer::Send` API accepts parameters as views (`string_view`,
`zstring_view`, `HeaderViews`). These views point to data owned by the caller.
Inside `Send`, raw pointers from these views are passed down to `librdkafka`,
which stores them in its internal asynchronous queues for background delivery.

If a client connection drops, a cascade cancellation of tasks occurs. This
leads to a premature return from `kafka::Producer::Send`, causing the caller
to destroy the original payload memory. However, `librdkafka` still retains
these pointers and attempts to access them during subsequent network flushes
to the broker, resulting in memory corruption.

To fix this, we ensure that `Producer::Send` is blocked from returning
prematurely and cannot exit until the background `SendImpl` completes
its interaction with `librdkafka`.

Co-authored-by: Aleksander Rypalov <rypalov2002@gmail.com>
@disaykin
disaykin force-pushed the kafka-producer-cancellation-fix branch from 30e88de to 82e223b Compare July 17, 2026 09:01
@disaykin

Copy link
Copy Markdown
Contributor Author

@apolukhin прошу ревью. проблему с установкой кафки в тестах я починил (там протух урл), но вот что делать с флапающим тестом на постгрю - не знаю:

[ RUN      ] PoolTests/PostgrePool.PoolServerUnavailable/Sync
userver/postgresql/src/storages/postgres/tests/pool_pgtest.cpp:313: Failure
Expected equality of these values:
  0
  stats.connection.active
    Which is: 1

[  FAILED  ] PoolTests/PostgrePool.PoolServerUnavailable/Sync, where GetParam() = 4-byte object <00-00 00-00> (2013 ms)

Но это точно не относится к моему фиксу.

Также проблема компиляции с rabbitmq на debian12 привнесена не мной/

@ArkadyRudenko

Copy link
Copy Markdown
Contributor

Tests in postgres is flap.

set -o errexit -o nounset -o pipefail -o posix -x

KAFKA_VERSION=4.0.1
KAFKA_VERSION=4.3.1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you increas it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Старая ссылка для установки кафки больше не работает. Заменил на ссылку для скачивания с их официального сайта

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Предлагаете оформить это отдельным запросом на влитие? Или претензия именно к изменению версии? Старая версия у меня вроде бы не захотела скачиваться по новой ссылке...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если у них на сайте теперь написано, что надо так, то окей

set -o errexit -o nounset -o pipefail -o posix -x

KAFKA_VERSION=4.0.1
KAFKA_VERSION=4.3.1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если у них на сайте теперь написано, что надо так, то окей

) const {
engine::TaskCancellationBlocker blocker;

if (engine::current_task::IsCancelRequested())

@ArkadyRudenko ArkadyRudenko Jul 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Добавь тест, в котором таска отменяется и происходит use-after-free. Чтобы хотя бы локально убедиться, что бага есть, а после фикса её нет

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это будет флаки тест. Даже при наличии бага, он будет фейлиться не каждый раз. И фэйлиться будет только под адрес-санитайзером. Я пробовал написать, получается как-то не очень... Попробую откопать свои наработки, может очередная попытка родит что-то удачнее

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

О, придумал. Можно выставить большие значения либрдкафка накопления очереди перед сбросом (1000 сообщений, 1 секунда, что раньше наступит). Тогда тест будет не флаки под санитайзером

@ArkadyRudenko ArkadyRudenko Jul 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вообще,я имел ввиду, чтобы ты показал нам тест и локально убедился, что он падает без твоего фикса. А с фиксом чинится (и потом его удалить и не мержить). Но если получится даже полноцнный сделать, то шикарно

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants